<template>
  <fx-affix :affix="affix" :offset="affixOffset" :scroll-target="scrollTarget">
    <div class="fx-anchor" ref="anchorDom">
      <div class="fx-anchor-line">
        <div class="fx-anchor-dot" :style="dotStyle"></div>
      </div>
      <slot/>
    </div>
  </fx-affix>
</template>

<script lang="ts">
import { computed, defineComponent, onBeforeMount, onBeforeUnmount, PropType, provide, ref } from 'vue'
import mitt, { Emitter } from 'mitt'
import { useCheckinPage } from '@/base/hooks'
import { dateFormat } from '@/base/utils'

interface AnchorProps {
  affix: boolean;
  affixOffset: number;
  scrollTarget: string;
  scrollOffset: number;
}

// 子组件返回自己监听的DOM数据
type AnchorLinkMittDomType = { dom: HTMLElement, id: string }

export default defineComponent({
  name: 'fxAnchor',
  props: {
    // 是否固定
    affix: {
      type: Boolean as PropType<boolean>,
      default: false
    },
    // 固定位置偏移量
    affixOffset: {
      type: Number as PropType<number>,
      default: 0
    },
    // 相对固定位置元素
    scrollTarget: {
      type: String as PropType<string>,
      default: 'body'
    },
    // 滚动偏移量
    scrollOffset: {
      type: Number as PropType<number>,
      default: 0
    }
  },
  setup (props: AnchorProps) {
    const anchorMitt: Emitter = mitt()
    const anchorDom = ref<HTMLElement>()
    const currentCheckedId = ref<string>('')
    const anchorLineDotIndex = ref<number>(0)
    const anchorLineDomList = ref<Array<AnchorLinkMittDomType>>([])

    provide('anchorMittEmit', anchorMitt.emit)
    provide('currentCheckedId', computed(() => currentCheckedId.value))
    provide('scrollTarget', computed(() => props.scrollTarget))
    provide('scrollOffset', computed(() => props.scrollOffset))

    const dotStyle = computed(() => {
      return {
        top: `${anchorLineDotIndex.value * 26 + 4}px`
      }
    })
    // 子组件点击后处理事件
    const handleAnchorLink = (id: string | undefined): void => {
      if (!id || currentCheckedId.value === id || !anchorDom.value) return
      currentCheckedId.value = id
      // 滚动点移动位置
      const children = Array.from(anchorDom.value?.getElementsByClassName('fx-anchor-link')) as HTMLElement[]
      anchorLineDotIndex.value = children.findIndex((item: HTMLElement) => item.dataset.id === id)
    }

    const handleAnchorLinkDom = (data: { domId: string, id: string } | undefined): void => {
      if (!data) return
      const dom = document.querySelector(data.domId) as HTMLElement
      dom && anchorLineDomList.value.push({ dom, id: data.id })
    }

    const handleScroll = () => {
      const scrollTargetDom = document.querySelector(props.scrollTarget)
      if (scrollTargetDom && anchorDom.value) {
        const { y } = scrollTargetDom.getBoundingClientRect()
        const sortData = anchorLineDomList.value.sort((a: AnchorLinkMittDomType, b: AnchorLinkMittDomType) => b.dom.offsetTop - a.dom.offsetTop)
        const currentData = sortData.find((item: AnchorLinkMittDomType) => scrollTargetDom.scrollTop >= (item.dom.offsetTop - y))
        handleAnchorLink(currentData?.id)
      }
    }

    useCheckinPage(() => {
      const scrollTargetDom = document.querySelector(props.scrollTarget)
      if (scrollTargetDom) scrollTargetDom.addEventListener('scroll', handleScroll)
    })

    onBeforeMount(() => {
      anchorMitt.on<string | undefined>('anchorLink', handleAnchorLink)
      anchorMitt.on<{ domId: string, id: string } | undefined>('anchorLinkDom', handleAnchorLinkDom)
    })

    onBeforeUnmount(() => {
      const scrollTargetDom = document.querySelector(props.scrollTarget)
      if (scrollTargetDom) scrollTargetDom.removeEventListener('scroll', handleScroll)
      anchorMitt.off<string | undefined>('anchorLink', handleAnchorLink)
      anchorMitt.off<{ domId: string, id: string } | undefined>('anchorLinkDom', handleAnchorLinkDom)
    })

    return {
      anchorDom,
      dotStyle
    }
  }
})
</script>
